package com.tianji.promotion.service.impl;

import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.plugins.pagination.PageDto;
import com.tianji.common.domain.dto.PageDTO;
import com.tianji.common.exceptions.BadRequestException;
import com.tianji.common.utils.BeanUtils;
import com.tianji.common.utils.ObjectUtils;
import com.tianji.promotion.domain.enums.CouponStatus;
import com.tianji.promotion.domain.po.Coupon;
import com.tianji.promotion.domain.po.ExchangeCode;
import com.tianji.promotion.domain.query.CodeQuery;
import com.tianji.promotion.domain.vo.CouponCodeVO;
import com.tianji.promotion.domain.vo.CouponScopeVO;
import com.tianji.promotion.mapper.CouponMapper;
import com.tianji.promotion.mapper.ExchangeCodeMapper;
import com.tianji.promotion.service.ICouponService;
import com.tianji.promotion.service.IExchangeCodeService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.tianji.promotion.utils.CodeUtil;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundValueOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;

import static com.tianji.promotion.constants.PromotionConstants.COUPON_CODE_SERIAL_KEY;
import static com.tianji.promotion.constants.PromotionConstants.COUPON_RANGE_KEY;

/**
 * <p>
 * 兑换码 服务实现类
 * </p>
 *
 * @author author
 * @since 2024-11-20
 */
@Service
public class ExchangeCodeServiceImpl extends ServiceImpl<ExchangeCodeMapper, ExchangeCode> implements IExchangeCodeService {
    private final StringRedisTemplate redisTemplate;
    private final BoundValueOperations<String, String> serialOps;
    @Autowired
    private CouponMapper couponMapper ;

    public ExchangeCodeServiceImpl(StringRedisTemplate redisTemplate) {
        this.redisTemplate = redisTemplate;
        this.serialOps = redisTemplate.boundValueOps(COUPON_CODE_SERIAL_KEY);
    }

    /**
     * 异步生成优惠券兑换码
     *
     * @param coupon
     */
    @Override
    @Async("generateExchangeCodeExecutor")
    public void asyncGenerateCode(Coupon coupon) {
        //1.发放数量
        Integer totalNum = coupon.getTotalNum();
        //2.获取redis自增序列号
        Long increment = serialOps.increment(totalNum);
        if (increment == null) {
            return;
        }
        //3.循环生成兑换码
        int max = increment.intValue();
        ArrayList<ExchangeCode> list = new ArrayList<>(totalNum);
        for (int i = max - totalNum + 1; i <= max; i++) {
            String code = CodeUtil.generateCode(i, coupon.getId());
            ExchangeCode ec = new ExchangeCode();
            ec.setCode(code);
            ec.setExchangeTargetId(coupon.getId());
            ec.setId(i);
            ec.setExpiredTime(coupon.getTermEndTime());
            list.add(ec);
        }
        //4.批量插入到数据库中
        saveBatch(list);

        // 5.写入Redis缓存，member：couponId，score：兑换码的最大序列号
        redisTemplate.opsForZSet().add(COUPON_RANGE_KEY, coupon.getId().toString(), max);
    }

    /**
     * 分页查看兑换码
     *
     * @param query
     * @return
     */
    @Override
    public PageDTO<CouponCodeVO> queryCouponScopeList(CodeQuery query) {
        //先查询兑换码状态
        LambdaQueryWrapper<Coupon> wrapper = new LambdaQueryWrapper<Coupon>().eq(Coupon::getId, query.getCouponId());
        Coupon coupon = couponMapper.selectOne(wrapper);
        if (ObjectUtils.isEmpty(coupon)){
            throw new BadRequestException("优惠券状态不正确或者不存在该优惠券");
        }
        //查询兑换码
        Page<ExchangeCode> page1 = this.lambdaQuery()
                .eq(ExchangeCode::getExchangeTargetId, query.getCouponId())
                .eq(ExchangeCode::getStatus, query.getStatus())
                .page(query.toMpPageDefaultSortByCreateTimeDesc());
        List<ExchangeCode> records = page1.getRecords();
        if (ObjectUtils.isEmpty(records)) {
            return PageDTO.empty(page1);
        }

        //封装对象
        List<CouponCodeVO> couponCodeVOS = BeanUtils.copyList(records, CouponCodeVO.class);

        return PageDTO.of(page1, couponCodeVOS);
    }

    @Override
    public Boolean updateExchangeMark(long serialNum, boolean b) {
        Boolean b1 = redisTemplate.opsForValue().setBit(COUPON_CODE_SERIAL_KEY, serialNum, b);
        return b1 != null && b1;
    }


}
